package com.xxb.ds.aop;

import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Stack;

/**
 * @author panghu
 * @version 1.0.0
 * @createTime 2023年07月09日 14:50:00
 * @Description
 */
@Slf4j
@Aspect
@Component
public class DataSourceTransactionalAspect {
    private static final ThreadLocal<Stack<Pair<PlatformTransactionManager, TransactionStatus>>> THREAD_LOCAL = new ThreadLocal<>();

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 事务声明
     */
    private DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    {
        // 非只读模式
        def.setReadOnly(false);
        // 事务隔离级别：采用数据库的
        def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        // 事务传播行为
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    }

    @Pointcut("@annotation(com.xxb.ds.aop.XxbTransactional)")
    public void txpointcut() {
    }

    /**
     * 声明事务 - 前置通知
     */
    @Before("txpointcut()")
    public void before(JoinPoint joinPoint) {
        // 根据设置的事务名称按顺序声明，并放到ThreadLocal里
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        XxbTransactional xxbTransactional = methodSignature.getMethod().getAnnotation(XxbTransactional.class);
        String[] transactionManagerNames = xxbTransactional.values();
        Stack<Pair<PlatformTransactionManager, TransactionStatus>> pairStack = new Stack<>();
        for (String transactionManagerName : transactionManagerNames) {
            PlatformTransactionManager transactionManager;
            // TODO 名字定义为枚举
            if ("transactionManager".equals(transactionManagerName)) {
                transactionManager = (PlatformTransactionManager) applicationContext.getBean(transactionManagerName, "transactionManager");
            } else if ("historytransactionManager".equals(transactionManagerName)) {
                transactionManager = (PlatformTransactionManager) applicationContext.getBean(transactionManagerName, "historytransactionManager");
            } else {
                transactionManager = (PlatformTransactionManager) applicationContext.getBean(transactionManagerName, "transactionManager");
            }
            TransactionStatus transactionStatus = transactionManager.getTransaction(def);
            pairStack.push(new Pair(transactionManager, transactionStatus));
            THREAD_LOCAL.set(pairStack);
        }
    }

    /**
     * 提交事务
     */
    @AfterReturning("txpointcut()")
    public void afterReturning() {
        // 栈顶弹出（后进先出）
        Stack<Pair<PlatformTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get();
        while (!pairStack.empty()) {
            Pair<PlatformTransactionManager, TransactionStatus> pair = pairStack.pop();
            pair.getKey().commit(pair.getValue());
        }
        THREAD_LOCAL.remove();
    }

    /**
     * 回滚事务
     */
    @AfterThrowing(value = "txpointcut()")
    public void afterThrowing() {
        // 栈顶弹出（后进先出）
        Stack<Pair<PlatformTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get();
        while (!pairStack.empty()) {
            Pair<PlatformTransactionManager, TransactionStatus> pair = pairStack.pop();
            pair.getKey().rollback(pair.getValue());
        }
        THREAD_LOCAL.remove();
    }
}
